package com.example.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.example.lotterysystem.common.config.ExecutorConfig;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtils;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.common.utils.SMSUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.WinningRecordDO;
import com.example.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.dao.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
public class MyReceiver {
    @Autowired
    private DrawPrizeService drawPrizeService;

    @Autowired
    private ActivityService activityService;
    @Autowired
    private ActivityStatusManager activityStatusManager;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private SMSUtil smsUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;


    private final static Logger logger = LoggerFactory.getLogger(MyReceiver.class);
    @RabbitHandler
    @Transactional(rollbackFor = Exception.class)
    public void process(Map<String,String> message) throws Exception {
        logger.info("mq接收到数据 message={}", JacksonUtils.writeValueString(message));
        String paramString =  message.get("messageData");
        DrawPrizeParam param =  JacksonUtils.readValue(paramString,DrawPrizeParam.class);


        //处理抽奖流程
        try {

            //校验抽奖请求参数是否有效
            if(!drawPrizeService.checkDrawPrizeParam(param)){
                return;
            }
            //状态扭转处理

            statusConvert(param);

            //保存中奖者信息

            List<WinningRecordDO> winningRecordDOList = drawPrizeService.saveWinningRecords(param);

            //短信邮箱通知
            //并发处理
            syncExecute(winningRecordDOList);

        }catch (ServiceException e) {
           logger.error("处理MQ信息异常！{}{}",e.getCode(),e.getMessage(),e);
           //需要保证事务一致性 回滚事务
            rollback(param);
           throw e;
        }catch (Exception e) {
           logger.error("处理MQ信息异常！{}{}",e);
            // 需要保证事务一致性 回滚事务
            rollback(param);
            throw e;
        }

        //异常 保证事务一致性（回滚）

    }

    /**
     * 回滚异常
     * @param param
     */
    private void rollback(DrawPrizeParam param) {
        //1.回滚状态扭转
        //状态是否需要回滚
        if(!statusNeedRollback(param)) {
            return;
        }
        rollbackStatus(param);
        //2.回滚保存中奖记录
        if(!winnerNeedRollback(param)) {
            return;
        }
        rollbackWinner(param);
    }

    private boolean winnerNeedRollback(DrawPrizeParam param) {
        //看看是否中奖名称里有没有中奖
        int count  = winningRecordMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        return count>0;
    }
    private void rollbackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }


    private boolean statusNeedRollback(DrawPrizeParam param) {
        // 活动 奖品 人员 状态是否扭转
        // 因为保证了事务的一致性 要么扭转了 要么没扭转（不包含活动）
        // 只需判断 人员/奖品是否扭转而已  活动不行
        // 再扭转状态 INIT
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //已经扭转
            return true;
        }
        return false;
    }

    /**
     * 恢复原始状态
     * @param param
     */
    private void rollbackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setActivityTargetStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setPrizeTargetStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.winner::getUserId)
                .collect(Collectors.toList()));
        convertActivityStatusDTO.setUserTargetStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandEvent( convertActivityStatusDTO);
    }

    /**
     * 并发处理 邮箱和短信发送
     * @param winningRecordDOList
     */
    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {
        //发送邮箱
        threadPoolTaskExecutor.execute(()->sendMail(winningRecordDOList));
        //发送短信
        threadPoolTaskExecutor.execute(()->sendMessage(winningRecordDOList));
    }

    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖名单为空！");
            return;
        }
        for (WinningRecordDO winningRecordDO:winningRecordDOList) {
            Map<String, String> map = new HashMap<>();
            map.put("name", winningRecordDO.getWinnerName());
            map.put("activityName", winningRecordDO.getActivityName());
            map.put("prizeTiers", ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage());
            map.put("prizeName", winningRecordDO.getPrizeName());
            map.put("winningTime", DateUtil.formatTime(winningRecordDO.getWinningTime()));
            smsUtil.sendMessage("SMS_474785242",
                    winningRecordDO.getWinnerPhoneNumber().getValue(),
                    JacksonUtils.writeValueString(map));
        }
    }

    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖名单为空！");
            return;
        }
       for(WinningRecordDO winningRecordDO:winningRecordDOList) {
           String context = "Hi，" + winningRecordDO.getWinnerName() + "。恭喜你在"
                   + winningRecordDO.getActivityName() + "活动中获得"
                   + ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                   + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                   + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";
           mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),
                   "中奖通知：",
                   context);
       }
    }

    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setActivityTargetStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setPrizeTargetStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.winner::getUserId)
                .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setUserTargetStatus(ActivityUserStatusEnum.COMPLETED);

        activityStatusManager.handEvent(convertActivityStatusDTO);

        //保存到缓存
        activityService.cacheActivity(param.getActivityId());



        //(设计模式) 责任链 策略模式
        //可以提供代码的灵活性 拓展性 维护性

        //1.奖品状态
        // 查询奖品的基本信息 判断是否是 COMPLETED 如果不是扭转状态


        //2.人员状态
        //
        //3. 活动状态  只有处理完了奖品 才能最终去处理活动

        //保存到缓存
    }
}
